package storage.tx.recovery;

import static storage.tx.recovery.LogRecord.*;
import server.starDB;
import storage.buffer.Buffer;
import storage.file.Block;

import java.util.*;


public class RecoveryMgr {
   private int txnum;
   
 
   public RecoveryMgr(int txnum) {
      this.txnum = txnum;
      new StartRecord(txnum).writeToLog();
   }
   
  
   public void commit() {
      starDB.bufferMgr().flushAll(txnum);
      int lsn = new CommitRecord(txnum).writeToLog();
      starDB.logMgr().flush(lsn);
   }
   
   
   public void rollback() {
      doRollback();
      starDB.bufferMgr().flushAll(txnum);
      int lsn = new RollbackRecord(txnum).writeToLog();
      starDB.logMgr().flush(lsn);
   }
   
 
   public void recover() {
      doRecover();
      starDB.bufferMgr().flushAll(txnum);
      int lsn = new CheckpointRecord().writeToLog();
      starDB.logMgr().flush(lsn);
      
   }
   
  
   public int setInt(Buffer buff, int offset, int newval) {
      int oldval = buff.getInt(offset);
      Block blk = buff.block();
      if (isTempBlock(blk))
         return -1;
      else
         return new SetIntRecord(txnum, blk, offset, oldval).writeToLog();
   }
   
  
   public int setString(Buffer buff, int offset, String newval) {
      String oldval = buff.getString(offset);
      Block blk = buff.block();
      if (isTempBlock(blk))
         return -1;
      else
         return new SetStringRecord(txnum, blk, offset, oldval).writeToLog();
   }
   
   
   private void doRollback() {
      Iterator<LogRecord> iter = new LogRecordIterator();
      while (iter.hasNext()) {
         LogRecord rec = iter.next();
         if (rec.txNumber() == txnum) {
            if (rec.op() == START)
               return;
            rec.undo(txnum);
         }
      }
   }
   
 
   private void doRecover() {
      Collection<Integer> committedTxs = new ArrayList<Integer>();
      Iterator<LogRecord> iter = new LogRecordIterator();
      while (iter.hasNext()) {
         LogRecord rec = iter.next();
         if (rec.op() == CHECKPOINT)
            return;
         if (rec.op() == COMMIT)
            committedTxs.add(rec.txNumber());
         else if (!committedTxs.contains(rec.txNumber()))
            rec.undo(txnum);
      }
   }
   
  
   private boolean isTempBlock(Block blk) {
      return blk.getFileName().startsWith("temp");
   }
}
